package miniproj.models.reservation;

import java.util.LinkedList;
import java.util.List;

import miniproj.models.groups.Group;
import miniproj.models.groups.Teaching;
import miniproj.models.people.Student;
import miniproj.models.people.Teacher;
import miniproj.models.room.Room;

/**
 * This class represents a Reservation of a classroom in the school. It concerns
 * a teacher, a date, a teaching, and groups of students.
 * 
 * @author Baptiste Viale, William Tassoux, Thibault Piketty and Victor Salle
 * 
 */
public class Reservation implements java.io.Serializable {

	private static final long serialVersionUID = -7872050651898729110L;

	private Teacher client;
	private Room room;
	private Date date;
	private List<Group> groups;
	private Teaching teaching;

	/**
	 * Instantiates a new reservation. (JavaBeans)
	 */
	public Reservation() {
	}

	/**
	 * Instantiates a new reservation.
	 * 
	 * @param client
	 *            the teacher who made the reservation
	 * @param date
	 *            the date of the reservation
	 * @param room
	 *            the reserved room
	 * @param teaching
	 *            the teaching concerned by this reservation
	 * @param group
	 *            the groups concerned by this reservation
	 */
	public Reservation(Teacher client, Date date, Room room, Teaching teaching,
			List<Group> group) {
		this.client = client;
		this.date = date;
		this.room = room;
		this.teaching = teaching;
		this.groups = group;
	}

	/**
	 * Return the teacher of the reservation.
	 * 
	 * @return the client the teacher who made the reservation
	 */
	public Teacher getClient() {
		return this.client;
	}

	/**
	 * Return the date of this reservation.
	 * 
	 * @return the date of the reservation
	 */
	public Date getDate() {
		return this.date;
	}

	/**
	 * Return the group of students of this reservation.
	 * 
	 * @return the groups concerned by this reservation
	 */
	public List<Group> getGroups() {
		return this.groups;
	}

	/**
	 * Return the room of this reservation.
	 * 
	 * @return the reserved room
	 */
	public Room getRoom() {
		return this.room;
	}

	/**
	 * Return the teaching of this Reservation.
	 * 
	 * @return the teaching concerned by this reservation
	 */
	public Teaching getTeaching() {
		return this.teaching;
	}

	@Override
	public boolean equals(Object o) {
		if (!(o instanceof Reservation))
			return false;

		Reservation r = (Reservation) o;

		if (!client.equals(r.client))
			return false;

		if (!room.equals(r.room))
			return false;

		if (!date.equals(r.date))
			return false;

		if (!teaching.equals(r.teaching))
			return false;

		return true;
	}

	@Override
	public String toString() {
		return "Reservation of room " + this.room.getId() + " by "
				+ this.client + " for group " + this.groups + ", teaching "
				+ this.teaching.getName() + ",  at " + this.getDate();
	}

	/**
	 * Order the given Reservations by date.
	 * 
	 * @param l
	 *            the list of reservations
	 * @return the sorted list of reservations
	 */
	@SuppressWarnings("unchecked")
	public static LinkedList<Reservation> orderByDate(LinkedList<Reservation> l) {
		LinkedList<Reservation> save = (LinkedList<Reservation>) l.clone();
		LinkedList<Reservation> orderedReservations = new LinkedList<Reservation>();

		if (l.size() == 0)
			return l;

		Reservation oldest;

		while (save.size() != 0) {
			oldest = save.getFirst();
			for (Reservation r : save) {
				if (r.getDate().before(oldest.getDate()))
					oldest = r;
			}
			orderedReservations.addLast(oldest);
			save.remove(oldest);
		}

		return orderedReservations;
	}

	/**
	 * Gets the reservations by teaching.
	 * 
	 * @param t
	 *            the teaching
	 * @param l
	 *            the list of reservations
	 * @return the reservations by teaching
	 */
	public static LinkedList<Reservation> getReservationsByTeaching(Teaching t,
			List<Reservation> l) {
		LinkedList<Reservation> rs = new LinkedList<Reservation>();

		for (Reservation r : l)
			if (r.getTeaching().equals(t))
				rs.add(r);

		return rs;
	}

	/**
	 * Sets the client.
	 * 
	 * @param client
	 *            the new client of the reservation
	 */
	public void setClient(Teacher client) {
		this.client = client;
	}

	/**
	 * Sets the room.
	 * 
	 * @param room
	 *            the new room concerned by the reservation
	 */
	public void setRoom(Room room) {
		this.room = room;
	}

	/**
	 * Sets the date.
	 * 
	 * @param date
	 *            the new date of the reservation
	 */
	public void setDate(Date date) {
		this.date = date;
	}

	/**
	 * Sets the teaching.
	 * 
	 * @param teaching
	 *            the new teaching concerned by the reservation
	 */
	public void setTeaching(Teaching teaching) {
		this.teaching = teaching;
	}

	/**
	 * Sets the groups.
	 * 
	 * @param groups
	 *            the new groups concerned by this reservation
	 */
	public void setGroups(List<Group> groups) {
		this.groups = groups;
	}

	/**
	 * Gets the available rooms.
	 * 
	 * @param date
	 *            the date
	 * @param nbStudents
	 *            the number of students
	 * @param rooms
	 *            the list of rooms
	 * @param reservations
	 *            the current list of reservations
	 * @return the available rooms at this date
	 */
	public static LinkedList<Room> getAvailableRooms(Date date, int nbStudents,
			List<Room> rooms, List<Reservation> reservations) {
		LinkedList<Room> availableRooms = new LinkedList<Room>();

		for (Room room : rooms) {
			boolean isIncorrect = false;
			if (room.getCapacity() < nbStudents)
				isIncorrect = true;

			for (Reservation r : reservations)
				if (r != null)
					if ((r.getRoom().equals(room) && r.getDate().equals(date)))
						isIncorrect = true;

			if (!isIncorrect)
				availableRooms.add(room);
		}

		return availableRooms;
	}

	/**
	 * Gets the most optimized room.
	 * 
	 * @param reservations
	 *            the list of reservations
	 * @param rooms
	 *            the list of rooms
	 * @param nbStudents
	 *            the number of students
	 * @param date
	 *            the date at which the reservation will be made
	 * @return the optimized room for the parameters
	 */
	public static Room getOptimizedRoom(List<Reservation> reservations,
			List<Room> rooms, int nbStudents, Date date) {
		Room bestRoom = null;
		int bestDiff = Integer.MAX_VALUE;

		LinkedList<Room> availableRooms = getAvailableRooms(date, nbStudents,
				rooms, reservations);

		for (Room r : availableRooms) {
			if (r.getCapacity() < nbStudents)
				continue;

			if ((r.getCapacity() - nbStudents) < bestDiff) {
				bestRoom = r;
				bestDiff = r.getCapacity() - nbStudents;
			}
		}

		return bestRoom;
	}

	/**
	 * Filter reservations by group.
	 * 
	 * @param reservations
	 *            the list of reservations
	 * @param group
	 *            the group
	 * @return the linked list of reservations made for the specific group
	 */
	public static LinkedList<Reservation> filterReservationsByGroup(
			LinkedList<Reservation> reservations, Group group) {
		LinkedList<Reservation> filteredReservations = new LinkedList<Reservation>();
		;

		for (Reservation reservation : reservations)
			if (reservation.getGroups().contains(group))
				filteredReservations.add(reservation);

		return filteredReservations;
	}

	/**
	 * Filter reservations by student.
	 * 
	 * @param reservations
	 *            the list of reservations
	 * @param student
	 *            the student
	 * @return the linked list of reservations concerning the current student
	 */
	public static LinkedList<Reservation> filterReservationsByStudent(
			LinkedList<Reservation> reservations, Student student) {
		LinkedList<Reservation> filteredReservations = new LinkedList<Reservation>();
		;

		for (Reservation reservation : reservations)
			for (Group group : reservation.getGroups())
				if (group.getStudents().contains(student))
					filteredReservations.add(reservation);

		return filteredReservations;
	}

	/**
	 * Filter reservations by teacher.
	 * 
	 * @param reservations
	 *            the list of reservations
	 * @param teacher
	 *            the teacher
	 * @return the linked list of reservations concerning the specified teacher
	 */
	public static LinkedList<Reservation> filterReservationsByTeacher(
			LinkedList<Reservation> reservations, Teacher teacher) {
		LinkedList<Reservation> filteredReservations = new LinkedList<Reservation>();

		for (Reservation reservation : reservations)
			if (reservation.getClient().equals(teacher))
				filteredReservations.add(reservation);

		return filteredReservations;
	}
}
